abstract SpecialInt: Int;
abstract SpecialFloat: Float;

function f(a: SpecialInt, b: SpecialFloat, c: CString) {
    printf("hello %i %.1f %s!\n", a, b, c);
}

function main() {
    // numeric types aren't a great example of implicits; for the test to pass,
    // using abstracts so they don't unify with each other
    var a: SpecialInt = 1;
    var b: SpecialFloat = 2.0;
    var c: CString = "world2";

    using implicit a {
        f(3.5, "world");
    }

    // implicit order shouldn't matter
    using implicit b, implicit a {
        f("world");

        // nested implicit
        using implicit c f();
    }

    var d: Float = 2.5;
    var e: Float;
    using implicit d {
        e = 4 + (implicit Float);
    }
    printf("%.1f\n", e);

    // if we cast e to an Int and promote to SpecialInt, this works
    using implicit ((e as Int) as SpecialInt) {
        f(5.1, "world");
    }

    // this does not use the implicit
    using implicit (e as Int) {
        f(a, 5.2, "world");
    }
}
